import Node from "./node";
import type { ILinkedList } from "../types";

export function defaultEquals<T>(a: T, b: T): boolean {
  return a === b;
}

class LinkedList<T> implements ILinkedList<T> {
  private count: number;
  protected head: Node<T> | undefined;

  constructor(equals = defaultEquals) {
    this.count = 0;
    this.head = undefined;
    this.equals = equals;
  }

  indexOf(element: T): number | undefined {
    let current = this.head;
    let i = 0,
      flag = false;
    while (current) {
      if (this.equals(current.value, element)) {
        flag = true;
        break;
      }
      i++;
      current = current.next;
    }
    return flag ? i : -1;
  }

  equals(_a: T, _b: T): boolean {
    return false;
  }

  getElementAt(index: number): Node<T> | undefined {
    if (index < 0 || index >= this.count) {
      return void 0;
    }
    if (index === 0) {
      return this.head;
    }
    let current = this.head;
    for (let i = 0; i < index; i++) {
      current = current?.next;
    }
    return current;
  }

  getValueAt(index: number): T | undefined {
    return this.getElementAt(index)?.value;
  }

  insert(element: T, index: number) {
    let new_node = new Node<T>();
    new_node.value = element;
    if (index > this.count || index < 0) {
      throw new Error("index error");
    }
    this.count++;
    let p_node: Node<T> | undefined = void 0;
    let n_node: Node<T> | undefined = void 0;
    if (index === 0) {
      new_node.next = this.head;
      this.head = new_node;
      return;
    }
    p_node = this.getElementAt(index - 1) as Node<T>;
    n_node = p_node.next;
    p_node.next = new_node;
    new_node.next = n_node;
  }

  push(element: T) {
    let n_node = new Node<T>();
    n_node.value = element;
    this.count++;
    if (this.isEmpty()) {
      this.head = n_node;
      return;
    }
    let lastNode = this.getElementAt(this.count - 1) as Node<T>;
    lastNode.next = n_node;
  }

  remove(element: T) {
    const index = this.indexOf(element);
    return index === -1 ? void 0 : this.removeAt(index as number);
  }

  removeAt(index: number) {
    if (this.isEmpty() || index < 0 || index >= this.count) {
      return void 0;
    }
    let now_node = this.getElementAt(index);
    let p_node = this.getElementAt(index - 1);
    let n_node = now_node?.next;
    if (index === 0) {
      this.head = n_node;
    }
    if (p_node) {
      p_node.next = n_node;
    }
    this.count--;
    return now_node?.value;
  }

  isEmpty() {
    return this.count === 0;
  }

  size() {
    return this.count;
  }

  clear(): void {
    this.count = 0;
    this.head = undefined;
  }

  toString() {
    let str = "";
    let node = this.head;
    while (node) {
      str += node.value;
      str += ",";
      node = node.next;
    }
    str = str.slice(0, -1);
    return str;
  }
}
export default LinkedList;
